package com.gqh.weather.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gqh.weather.auth.UserContext;
import com.gqh.weather.config.FileConfig;
import com.gqh.weather.config.MinioConfig;
import com.gqh.weather.domain.dto.*;
import com.gqh.weather.domain.entity.*;
import com.gqh.weather.exception.BusinessException;
import com.gqh.weather.exception.Result;
import com.gqh.weather.mapper.PcSubtaskMapper;
import com.gqh.weather.mapper.TaskMapper;
import com.gqh.weather.mapper.StudentMapper;
import com.gqh.weather.mapper.CityMapper;
import com.gqh.weather.service.PcSubtaskService;
import com.gqh.weather.service.AccountService;
import com.gqh.weather.service.SchoolService;
import com.gqh.weather.service.TaskSchoolRecordService;
import com.gqh.weather.util.MinioUtil;
import com.gqh.weather.util.ValidationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import io.minio.MinioClient;
import io.minio.PutObjectArgs;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDFont;

/**
 * 任务管理服务类
 */
@Slf4j
@Service
public class TaskService extends ServiceImpl<TaskMapper, Task> {

    @Autowired
    private TaskMapper taskMapper;
    
    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentService studentService;
    
    @Autowired
    private FileConfig fileConfig;
    
    @Autowired
    private MinioUtil minioUtil;
    
    @Autowired
    private MinioConfig minioConfig;
    
    @Autowired
    private MinioClient minioClient;
    
    @Autowired
    @Lazy
    private PcSubtaskService pcSubtaskService;

    @Autowired
    private PcSubtaskMapper pcSubtaskMapper;
    
    @Autowired
    private AccountService accountService;
    
    @Autowired
    private CityMapper cityMapper;
    
    @Autowired
    private SchoolService schoolService;
    
    @Autowired
    private TaskSchoolRecordService taskSchoolRecordService;

    /**
     * 创建评价任务
     *
     * @param request   创建请求
     * @return 创建结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> createTask(TaskCreateRequest request) {
        try {
            // 业务验证
            validateTaskRequest(request);
            
            // 检查任务名称是否重复
            if (isTaskNameExists(request.getName())) {
                return Result.error("任务名称已存在");
            }
            
            // 构建Task对象
            Task task = new Task();
            task.setName(request.getName());
            task.setSchoolYear(request.getSchool_year());
            task.setGrade(request.getGrade());
            task.setType(request.getType());
            task.setTotalScore(request.getTotal_score());
            task.setContent(request.getContent());
            task.setStartAt(request.getStart_at());
            task.setEndAt(request.getEnd_at());
            task.setSchoolState(0); // 默认未锁定
            task.setStudentState(0); // 默认未锁定
            task.setState(0); // 默认未发布
            task.setCreatedBy(UserContext.getCurrentUserId());
            task.setIsDeleted(0); // 默认未删除

            // 保存任务
            boolean success = save(task);
            if (!success) {
                return Result.error("创建任务失败");
            }

            boolean subtaskSuccess = pcSubtaskService.createSubtask(task.getId(), request.getSub_type(), request.getSchedule());
            if (!subtaskSuccess) {
                return Result.error("创建任务失败");
            }

            return Result.success(null);
        } catch (BusinessException e) {
            return Result.error(e.getMessage());
        } catch (Exception e) {
            log.error("创建任务失败", e);
            return Result.error("系统错误");
        }
    }

    /**
     * 更新任务信息
     *
     * @param request 任务对象
     * @return 更新结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> updateTask(TaskCreateRequest request) {
        try {
            if (request.getId() == null) {
                return Result.error("任务ID不能为空");
            }

            // 业务验证
            validateTaskRequest(request);
            
            // 检查任务是否存在
            Task existTask = getById(request.getId());
            if (existTask == null || existTask.getIsDeleted() == 1) {
                return Result.error("任务不存在");
            }

            // 检查子任务是否已经维护阅卷老师
            PcSubtask pcSubtask = pcSubtaskMapper.selectByPid(request.getId());
            if (StringUtils.hasText(pcSubtask.getSchedule())) {
                return Result.error("已经维护阅卷老师无法修改");
            }

            // 只允许修改部分字段
            Task updateTask = new Task();
            updateTask.setId(request.getId());
            updateTask.setName(request.getName());
            updateTask.setSchoolYear(request.getSchool_year());
            updateTask.setGrade(request.getGrade());
            updateTask.setType(request.getType());
            updateTask.setTotalScore(request.getTotal_score());
            updateTask.setContent(request.getContent());
            updateTask.setStartAt(request.getStart_at());
            updateTask.setEndAt(request.getEnd_at());
            
            // 更新任务
            boolean success = updateById(updateTask);
            if (!success) {
                return Result.error("更新任务失败");
            }

            LambdaUpdateWrapper<PcSubtask> wrapper = Wrappers.lambdaUpdate(PcSubtask.class)
                    .set(PcSubtask::getType, request.getSub_type())
                    .set(PcSubtask::getSchedule, request.getSchedule())
                    .eq(PcSubtask::getTaskPid, request.getId());

            pcSubtaskService.update(wrapper);

            return Result.success(null);
            
        } catch (Exception e) {
            log.error("更新任务失败", e);
            return Result.error("系统错误");
        }
    }

    /**
     * 统计分数
     *
     * @param taskId 任务ID
     * @param createdBy 创建者ID
     * @return 统计结果
     */
    private boolean statisticScore(Integer taskId, Integer createdBy) {
        try {
            // 获取任务信息
            Task task = getById(taskId);
            if (task == null) {
                log.error("任务不存在，任务ID: {}", taskId);
                return false;
            }
            
            // 获取账户信息获取citycode
            Account account = accountService.getById(createdBy);
            if (account == null) {
                log.error("账户不存在，账户ID: {}", createdBy);
                return false;
            }
            
            String citycode = account.getCitycode();
            if (citycode == null || citycode.isEmpty()) {
                log.error("账户城市码为空，账户ID: {}", createdBy);
                return false;
            }
            
            // 获取子区域城市码
            List<String> codes = cityMapper.selectChildrenCodesByCode(citycode);
            codes.add(citycode); // 添加当前城市码
            
            // 获取学校列表
            LambdaQueryWrapper<School> schoolWrapper = new LambdaQueryWrapper<>();
            schoolWrapper.in(School::getCitycode, codes);
            List<School> schoolList = schoolService.list(schoolWrapper);
            
            if (schoolList.isEmpty()) {
                log.warn("没有找到符合条件的学校，任务ID: {}, 城市码: {}", taskId, citycode);
                return true; // 没有学校不算是错误
            }
            
            // 并发统计每个学校的分数
            // 这里简化处理，实际应该根据Go代码中的TaskSchoolRecord.statisticSchool方法实现
            for (School school : schoolList) {
                // 调用TaskSchoolRecordService的statisticSchool方法
                log.info("统计学校分数，任务ID: {}, 学校ID: {}", taskId, school.getId());
                
                try {
                    // 调用具体的统计方法
                    boolean success = taskSchoolRecordService.statisticSchool(createdBy, taskId, school.getId());
                    if (!success) {
                        log.error("统计学校分数失败，任务ID: {}, 学校ID: {}", taskId, school.getId());
                    }
                } catch (Exception e) {
                    log.error("统计学校分数失败，任务ID: {}, 学校ID: {}", taskId, school.getId(), e);
                }
            }
            
            return true;
        } catch (Exception e) {
            log.error("统计分数失败，任务ID: " + taskId, e);
            return false;
        }
    }

    /**
     * 修改任务状态
     *
     * @param taskId 任务ID
     * @param state  状态：0未发布 1已发布 2结束
     * @return 修改结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> updateTaskState(Integer taskId, Integer state) {
        try {
            if (state == null || state < 0 || state > 2) {
                return Result.error("状态参数错误");
            }
            
            Task task = getById(taskId);
            if (task == null || task.getIsDeleted() == 1) {
                return Result.error("任务不存在");
            }
            
            // 验证状态转换规则
//            if (!isValidStateTransition(task.getState(), state)) {
//                return Result.error("状态转换不合法");
//            }
            
            // 如果状态变为2（结束），需要结束子任务并统计分数
            if (state == 2) {
                // 结束子任务
                // 注意：这里需要根据Go代码逻辑实现，Go代码中调用了PcSubtask{}.EndByPid(r.Id, r.State)
                // 在Java中我们通过PcSubtaskService来处理
                if (!pcSubtaskService.endSubtaskByTaskPid(taskId, state)) {
                    return Result.error("结束子任务失败");
                }
                
                // 统计分数
                // 注意：这里需要根据Go代码逻辑实现，Go代码中调用了t.statisticScore(r.Id)
                if (!statisticScore(taskId, task.getCreatedBy())) {
                    return Result.error("统计分数失败");
                }
            }
            
            // 如果状态从0变为1（未发布->已发布），需要生成准考证并创建子任务
            if (task.getState() == 0 && state == 1) {
//                // 生成学生准考证
//                Result<String> ticketResult = genStudentAdmissionTicket(taskId);
//                if (ticketResult.getCode() != 200) {
//                    return Result.error("生成准考证失败: " + ticketResult.getMsg());
//                }
//
//                // 更新准考证URL
//                task.setAdmissionTicketUrl(ticketResult.getData());

                // 创建子任务

                PcSubtask pcSubtask = pcSubtaskMapper.selectByPid(taskId);

                if (pcSubtask.getYjState() != 1) {
                    return Result.error("请先设置阅卷老师");
                }

                Long studentNum = studentMapper.selectCount(Wrappers.lambdaQuery(Student.class).eq(Student::getTaskId, taskId));
                if (studentNum < 1) {
                    return Result.error("请先设置考生");
                }

                studentService.lockStudents(taskId);

                pcSubtaskService.updatePcSubtaskState(pcSubtask.getId(), 1);

            }

            task.setState(state);
            boolean success = updateById(task);
            
            if (success) {
                return Result.success(null);
            } else {
                return Result.error("修改任务状态失败");
            }
            
        } catch (Exception e) {
            log.error("修改任务状态失败", e);
            return Result.error("系统错误");
        }
    }

    /**
     * 删除任务
     *
     * @param id 任务ID
     * @return 删除结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Void> deleteTask(List<Integer> id) {
        try {
            
            boolean success = removeByIds(id);
            
            if (success) {
                return Result.success(null);
            } else {
                return Result.error("删除任务失败");
            }
            
        } catch (Exception e) {
            log.error("删除任务失败", e);
            return Result.error("系统错误");
        }
    }

    /**
     * 分页查询任务列表
     *
     * @param pageRequest 分页请求
     * @param createdBy   创建者ID（权限过滤）
     * @return 查询结果
     */
    public Result<MyPage<Task>> getTaskList(PageRequest<Task> pageRequest, Integer createdBy) {
        try {
            Page<Task> page = pageRequest.toPage();
            
            // 获取关键字
            String keyword = StringUtils.hasText(pageRequest.getKeyword()) ? pageRequest.getKeyword() : null;
            
            // 使用自定义Mapper方法查询，带统计字段
            Page<Task> result = baseMapper.selectTaskListWithStats(page, createdBy, keyword, pageRequest.getState());

            List<Integer> ids = result.getRecords().stream().map(Task::getId).toList();

            Page<PcSubTaskListDataDTO> pg = pcSubtaskMapper.selectPcSubtaskListWithTaskInfo(new Page<>(1, pageRequest.getCount()), null, null, ids);
            Map<Integer, PcSubTaskListDataDTO> map = pg.getRecords().stream().collect(Collectors.toMap(PcSubTaskListDataDTO::getTaskPid, t -> t));

            // 计算task_state字段
            long currentTime = System.currentTimeMillis() / 1000; // 当前时间戳（秒）
            for (Task task : result.getRecords()) {
                task.setTaskState(calculateTaskState(task.getStartAt(), task.getEndAt(), currentTime));

                task.setSubTaskDTO(map.get(task.getId()));
            }
            
            MyPage<Task> myPage = MyPage.fromMybatisPlusPage(result);
            return Result.success(myPage);
            
        } catch (Exception e) {
            log.error("查询任务列表失败", e);
            return Result.error("查询失败");
        }
    }

    /**
     * 获取任务详情
     *
     * @param taskId 任务ID
     * @return 任务详情
     */
    public Result<Task> getTaskInfo(Integer taskId) {
        try {
            Task task = getById(taskId);
            if (task == null || task.getIsDeleted() == 1) {
                return Result.error("任务不存在");
            }
            long currentTime = System.currentTimeMillis() / 1000; // 当前时间戳（秒）

            task.setTaskState(calculateTaskState(task.getStartAt(), task.getEndAt(), currentTime));

            PcSubtask pcSubtask = pcSubtaskMapper.selectByPid(taskId);

            task.setSubTaskDTO(pcSubtaskMapper.selectPcSubtaskInfoWithTaskInfo(pcSubtask.getId()));
            
            return Result.success(task);
            
        } catch (Exception e) {
            log.error("获取任务详情失败", e);
            return Result.error("查询失败");
        }
    }

    // ========== 私有方法 ==========

    /**
     * 验证任务创建请求
     */
    private void validateTaskRequest(TaskCreateRequest request) throws BusinessException {
        // 验证年级
        if (!ValidationUtil.isValidGrade(request.getGrade())) {
            throw new BusinessException("年级参数错误");
        }
        
        // 验证测评方式
        if (request.getType() == null || (request.getType() != 0 && request.getType() != 1)) {
            throw new BusinessException("测评方式参数错误");
        }
        
        // 验证时间
        if (request.getStart_at() >= request.getEnd_at()) {
            throw new BusinessException("结束时间必须大于开始时间");
        }
        
        // 验证开始时间不能早于今天的0点
        Calendar today = Calendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 0);
        today.set(Calendar.MINUTE, 0);
        today.set(Calendar.SECOND, 0);
        today.set(Calendar.MILLISECOND, 0);
        long todayStart = today.getTimeInMillis() / 1000;
        if (request.getStart_at() < todayStart) {
            throw new BusinessException("开始时间不能早于今天的0点");
        }
    }

    /**
     * 检查任务名称是否存在
     */
    private boolean isTaskNameExists(String name) {
        LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Task::getName, name)
               .eq(Task::getIsDeleted, 0);
        return count(wrapper) > 0;
    }

    /**
     * 验证状态转换是否合法
     */
    private boolean isValidStateTransition(Integer currentState, Integer newState) {
        // 未发布 -> 已发布
        if (currentState == 0 && newState == 1) {
            return true;
        }
        // 已发布 -> 结束
        if (currentState == 1 && newState == 2) {
            return true;
        }
        // 已发布 -> 未发布（撤回）
        if (currentState == 1 && newState == 0) {
            return true;
        }
        
        return false;
    }

    /**
     * 计算任务动态状态
     * 
     * @param startAt 开始时间戳（秒）
     * @param endAt 结束时间戳（秒）
     * @param currentTime 当前时间戳（秒）
     * @return 0:未开始 1:进行中 2:已结束
     */
    private Integer calculateTaskState(Long startAt, Long endAt, long currentTime) {
        // 处理边界情况：时间为null或无效值
        if (startAt == null || endAt == null || startAt <= 0 || endAt <= 0) {
            return 0; // 默认未开始
        }
        
        if (currentTime < startAt) {
            return 0; // 未开始
        } else if (currentTime >= endAt) {
            return 2; // 已结束
        } else {
            return 1; // 进行中
        }
    }

//    /**
//     * 生成学生准考证
//     *
//     * @param taskId 任务ID
//     * @return 准考证文件URL
//     */
//    private Result<String> genStudentAdmissionTicket(Integer taskId) {
//        try {
//            // 获取学生准考证信息
//            List<StudentTicketDTO> students = studentMapper.selectStudentTicketList(taskId);
//            if (students.isEmpty()) {
//                return Result.error("该任务没有待考学生");
//            }
//
//            // 创建导出目录
//            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss"));
//            String exportDirPath = fileConfig.getExportDir() + "/admission_tickets/" + taskId + "_" + timestamp;
//            Path exportDir = Paths.get(exportDirPath);
//            if (!Files.exists(exportDir)) {
//                Files.createDirectories(exportDir);
//            }
//
//            // 按学校分组学生
//            Map<String, List<StudentTicketDTO>> schoolStudents = new HashMap<>();
//            Map<String, String> schoolCities = new HashMap<>();
//
//            for (StudentTicketDTO student : students) {
//                String schoolName = student.getSchoolName() != null ? student.getSchoolName() : "未知学校";
//                String cityName = student.getCityName() != null ? student.getCityName() : "未知城市";
//
//                schoolStudents.computeIfAbsent(schoolName, k -> new ArrayList<>()).add(student);
//                schoolCities.put(schoolName, cityName);
//            }
//
//            // 生成PDF文件
//            Map<String, String> pdfFiles = new HashMap<>();
//
//            for (Map.Entry<String, List<StudentTicketDTO>> entry : schoolStudents.entrySet()) {
//                String schoolName = entry.getKey();
//                List<StudentTicketDTO> schoolStudentList = entry.getValue();
//
//                // 生成PDF文件路径
//                String pdfFileName = schoolName + ".pdf";
//                String pdfFilePath = exportDirPath + "/" + pdfFileName;
//
//                // 生成真实的PDF文件
//                generateAdmissionTicketPdf(pdfFilePath, schoolName, schoolStudentList);
//
//                pdfFiles.put(schoolName, pdfFilePath);
//            }
//
//            // 生成zip包
//            String zipFileName = "准考证exam_" + taskId + ".zip";
//            String zipFilePath = exportDirPath + "/" + zipFileName;
//
//            try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
//                for (Map.Entry<String, String> entry : pdfFiles.entrySet()) {
//                    String schoolName = entry.getKey();
//                    String pdfFilePath = entry.getValue();
//                    String cityName = schoolCities.get(schoolName);
//
//                    // 添加文件到zip
//                    File pdfFile = new File(pdfFilePath);
//                    String zipEntryName = "准考证exam_" + taskId + "/" + cityName + "/" + pdfFile.getName();
//
//                    zipOut.putNextEntry(new ZipEntry(zipEntryName));
//                    Files.copy(pdfFile.toPath(), zipOut);
//                    zipOut.closeEntry();
//                }
//            }
//
//            // 上传到MinIO
//            File zipFile = new File(zipFilePath);
//            String objectName = "admission_tickets/" + taskId + "_" + timestamp + "/" + zipFileName;
//
//            try (FileInputStream fis = new FileInputStream(zipFile)) {
//                minioClient.putObject(
//                    PutObjectArgs.builder()
//                        .bucket(minioConfig.getBucketStudentFace())
//                        .object(objectName)
//                        .stream(fis, zipFile.length(), -1)
//                        .contentType("application/zip")
//                        .build()
//                );
//            }
//
//            // 返回MinIO访问URL
//            String minioUrl = "/files/" + minioConfig.getBucketStudentFace() + "/" + objectName;
//            return Result.success(minioUrl);
//
//        } catch (Exception e) {
//            log.error("生成学生准考证失败", e);
//            return Result.error("生成准考证失败: " + e.getMessage());
//        }
//    }
    
//    /**
//     * 生成准考证PDF文件
//     *
//     * @param filePath 文件路径
//     * @param schoolName 学校名称
//     * @param students 学生列表
//     */
//    private void generateAdmissionTicketPdf(String filePath, String schoolName, List<StudentTicketDTO> students) {
//        try (PDDocument document = new PDDocument()) {
//            // 创建页面 (A4尺寸: 595 x 842 points)
//            PDPage page = new PDPage();
//            document.addPage(page);
//
//            // 加载中文字体
//            PDFont chineseFont = null;
//            PDFont chineseFontBold = null;
//            try {
//                // 从resources/fonts目录加载字体文件
//                chineseFont = PDType0Font.load(document, getClass().getResourceAsStream("/fonts/simhei.ttf"));
//                chineseFontBold = PDType0Font.load(document, getClass().getResourceAsStream("/fonts/simhei.ttf"));
//            } catch (Exception e) {
//                log.warn("加载中文字体失败，使用默认字体: " + e.getMessage());
//                // 如果字体加载失败，回退到默认字体
//                chineseFont = PDType1Font.HELVETICA;
//                chineseFontBold = PDType1Font.HELVETICA_BOLD;
//            }
//
//            // 页面尺寸
//            float pageWidth = page.getMediaBox().getWidth();
//            float pageHeight = page.getMediaBox().getHeight();
//
//            // 设置边距
//            float marginLeft = 20;   // 左边距改回20
//            float marginRight = 20;  // 右边距改回20
//            float marginTop = 20;    // 上边距改回20
//
//            // 计算每个学生块的尺寸 (页面宽度减去左右边距后五等分)
//            float blockWidth = (pageWidth - marginLeft - marginRight) / 5;
//            float blockHeight = 160; // 增大高度以确保第三行也在虚线框内
//
//            // 创建内容流并绘制学生块的虚线框
//            try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
//                // 绘制学生块的虚线框
//                contentStream.setLineDashPattern(new float[]{3, 3}, 0); // 设置虚线模式
//                contentStream.setLineWidth(1);
//
//                int studentsPerPage = 20; // 每页最多20个学生块 (4行 x 5列)
//                int studentCount = Math.min(students.size(), studentsPerPage);
//
//                // 计算起始位置 (从页面顶部开始，考虑上边距)
//                float startX = marginLeft;
//                float startY = pageHeight - marginTop; // 考虑上边距
//
//                // 绘制虚线框
//                for (int i = 0; i < studentCount; i++) {
//                    int row = i / 5; // 行号 (0-3)
//                    int col = i % 5; // 列号 (0-4)
//
//                    float x = startX + col * blockWidth;
//                    float y = startY - row * blockHeight;
//
//                    // 绘制虚线框 (确保所有边框都正确绘制，路径闭合)
//                    contentStream.moveTo(x, y);
//                    contentStream.lineTo(x + blockWidth, y);
//                    contentStream.lineTo(x + blockWidth, y - blockHeight);
//                    contentStream.lineTo(x, y - blockHeight);
//                    contentStream.lineTo(x, y);
//                    contentStream.stroke();
//                }
//            } // 内容流在此处关闭
//
//            // 为每个学生生成并添加二维码和学号
//            int studentsPerPage = 20; // 每页最多20个学生块 (4行 x 5列)
//            int studentCount = Math.min(students.size(), studentsPerPage);
//
//            // 计算起始位置 (从页面顶部开始，考虑上边距)
//            float startX = marginLeft;
//            float startY = pageHeight - marginTop; // 考虑上边距
//
//            for (int i = 0; i < studentCount; i++) {
//                StudentTicketDTO student = students.get(i);
//                int row = i / 5; // 行号 (0-3)
//                int col = i % 5; // 列号 (0-4)
//
//                float x = startX + col * blockWidth;
//                float y = startY - row * blockHeight;
//
//                // 生成二维码内容
//                String qrContent = student.getStudentNo();
//
//                // 生成二维码图像 (稍微小于虚线框以确保边框可见)
//                int qrSize = (int)(blockWidth - 2); // 减少边距确保边框可见
//                BufferedImage qrImage = generateQRCodeImage(qrContent, qrSize, qrSize);
//
//                // 将图像添加到PDF中 (第一行是二维码，保留适当边距)
//                float qrX = x + 1; // 减少左边距确保左边框可见
//                float qrY = y - qrSize - 1; // 进一步减少顶部边距，使二维码更贴近虚线框顶部
//
//                addImageToPDF(document, qrImage, qrX, qrY);
//
//                // 添加姓名 (第二行)
//                try (PDPageContentStream textStream = new PDPageContentStream(document, page,
//                        PDPageContentStream.AppendMode.APPEND, true)) {
//                    textStream.setFont(chineseFont, 10);
//                    textStream.beginText();
//                    // 计算姓名文本位置 (在二维码下方，确保在虚线框内)
//                    float textY = y - qrSize - 20; // 在二维码下方，但仍在虚线框内
//                    textStream.newLineAtOffset(x + 5, textY);
//                    showTextSafely(textStream, student.getName(), chineseFont, 10.0f);
//                    textStream.endText();
//                }
//
//                // 添加学号 (第三行)
//                try (PDPageContentStream textStream = new PDPageContentStream(document, page,
//                        PDPageContentStream.AppendMode.APPEND, true)) {
//                    textStream.setFont(chineseFont, 10);
//                    textStream.beginText();
//                    // 计算学号文本位置 (在姓名下方，确保在虚线框内)
//                    float textY = y - qrSize - 35; // 在姓名下方，但仍在虚线框内
//                    textStream.newLineAtOffset(x + 5, textY);
//                    showTextSafely(textStream, student.getStudentNo(), chineseFont, 10.0f);
//                    textStream.endText();
//                }
//            }
//
//            // 保存文档
//            document.save(filePath);
//        } catch (IOException | WriterException e) {
//            log.error("生成PDF文件失败: " + filePath, e);
//        }
//    }
    
    /**
     * 检查字符是否被字体支持
     *
     * @param c 字符
     * @param font 字体
     * @return 是否支持
     */
    private boolean isCharacterSupported(char c, PDFont font) {
        try {
            // 对于常见的ASCII字符，直接返回true以提高性能
            if (c >= 32 && c <= 126) {
                return true;
            }
            // 对于中文字符范围，直接返回true（黑体应该支持）
            if (c >= 0x4E00 && c <= 0x9FFF) {
                return true;
            }
            // 对于Unicode字符，尝试编码
            font.encode(Character.toString(c));
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 安全地显示文本，避免不支持的字符导致异常
     *
     * @param contentStream 内容流
     * @param text 文本内容
     * @param font 字体
     * @param fontSize 字体大小
     * @throws IOException IO异常
     */
    private void showTextSafely(PDPageContentStream contentStream, String text, PDFont font, float fontSize) throws IOException {
        try {
            // 直接使用原始文本，不进行编码转换
            contentStream.setFont(font, fontSize);
            contentStream.showText(text);
        } catch (Exception e) {
            // 如果遇到任何异常，包括编码问题，过滤掉这些字符
            StringBuilder filteredText = new StringBuilder();
            for (char c : text.toCharArray()) {
                try {
                    // 检查字符是否可以被字体显示
                    if (isCharacterSupported(c, font)) {
                        filteredText.append(c);
                    } else {
                        // 如果字符不能被编码，替换为星号
                        filteredText.append("*");
                    }
                } catch (Exception ex) {
                    // 如果检查过程中出现异常，替换为星号
                    filteredText.append("*");
                }
            }
            contentStream.showText(filteredText.toString());
        }
    }
    
    /**
     * 将图像添加到PDF文档中
     *
     * @param document PDF文档
     * @param image 图像
     * @param x X坐标
     * @param y Y坐标
     * @throws IOException IO异常
     */
    private void addImageToPDF(PDDocument document, BufferedImage image, float x, float y) throws IOException {
        // 确保Y坐标不为负数
        if (y < 0) {
            y = 10; // 设置最小Y坐标
        }
        
        // 将BufferedImage转换为PDImageXObject
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        baos.flush();
        byte[] imageBytes = baos.toByteArray();
        baos.close();
        
        // 获取当前页
        PDPage page = document.getPage(document.getNumberOfPages() - 1);
        
        // 创建PDImageXObject
        org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject pdImage = 
            org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.createFromByteArray(document, imageBytes, "qr_code");
        
        // 创建内容流并添加图像
        try (PDPageContentStream contentStream = new PDPageContentStream(document, page, 
                PDPageContentStream.AppendMode.APPEND, true)) {
            contentStream.drawImage(pdImage, x, y, image.getWidth(), image.getHeight());
        }
    }
    
    /**
     * 生成二维码图像
     *
     * @param text 二维码内容
     * @param width 宽度
     * @param height 高度
     * @return 二维码图像
     * @throws WriterException 编码异常
     */
    private BufferedImage generateQRCodeImage(String text, int width, int height) throws WriterException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 禁用默认边距，使二维码图像填满整个区域
        hints.put(EncodeHintType.MARGIN, 0);
        BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hints);
        return MatrixToImageWriter.toBufferedImage(bitMatrix);
    }
    
//    /**
//     * 更新任务的考务包URL
//     *
//     * @param taskId 任务ID
//     * @param packageUrl 考务包URL
//     * @return 更新结果
//     */
//    public boolean updatePackageUrl(Integer taskId, String packageUrl) {
//        return taskMapper.updatePackageUrl(taskId, packageUrl) > 0;
//    }
//
//    /**
//     * 获取PcSubtaskService
//     *
//     * @return PcSubtaskService实例
//     */
//    public PcSubtaskService getPcSubtaskService() {
//        return pcSubtaskService;
//    }

}